home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / URL Helper II / Source / ParseState.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-14  |  13.9 KB  |  595 lines  |  [TEXT/MMCC]

  1. /***
  2.  * ParseState.c
  3.  *
  4.  *  Routines to handle the parse states
  5.  *
  6.  ***/
  7.  
  8. #include "debug_me.h"
  9. #include "Exceptions.h"
  10.  
  11. #include "URLHelperComponent.h"
  12. #include "URLHelperComponentPrivate.h"
  13.  
  14. /**
  15.  ** Structs to help with the parsing of the strings - at the moment we have a max of 10
  16.  ** items that can be subbed in one string.
  17.  **/
  18.  
  19. #define SUB_CHAR        '^'                            // the special replacement character
  20. #define MAX_PARSE_ITEMS    10                            // Max number of "^"s/per string
  21.  
  22. typedef struct {
  23.     short            repID;                            // ^0 is repID == 0
  24.     unsigned char    offset;                            // Position of the "^" in the string
  25.     unsigned char    length;                            // length of "^0" is 2.
  26. } parseReplaceItem, *parseReplaceItemPtr;
  27.  
  28. typedef struct {
  29.     short                validItems;                    // Number of items used.
  30.     parseReplaceItem    itemList[MAX_PARSE_ITEMS];    // The items...
  31. } parseReplaceList, *parseReplaceListPtr;
  32.  
  33. typedef struct URLHParseState {
  34.     short                validItems;                    // Number of items used
  35.     URLHMirrorListPtr    mirrorList;                    // What mirror list this guy is in...
  36.     Str255                baseURL;                    // URL string we parsed for this guy
  37.     parseReplaceItem    itemList[MAX_PARSE_ITEMS];    // The items
  38. } URLHParseState, *URLHParseStatePtr, **URLHParseStateHandle;
  39.  
  40. /**
  41.  ** Local routine definitions
  42.  **/
  43.  
  44. OSErr PSFindMirrorInList (URLHMirrorListPtr mList, StringPtr aURL, 
  45.                             URLHParseStatePtr pState);
  46.  
  47. OSErr PSMatchMirrorToURL (URLHMirrorListPtr mList, URLHMirrorPtr mirror, StringPtr aURL, 
  48.                             URLHParseStatePtr pState);
  49.  
  50. OSErr PSParseMirrorName (StringPtr name, parseReplaceListPtr itemList);
  51.  
  52. OSErr PBParseMatches (StringPtr name, parseReplaceListPtr itemList, StringPtr urlString,
  53.                         URLHParseStatePtr pState);
  54.  
  55. OSErr PSFindString (StringPtr string, short startOffset, short *foundPos,
  56.                         StringPtr findString, short findOffset, short findLength);
  57. /**
  58.  * _URLHelperNewParseState
  59.  *
  60.  *  We have a url thing and we now want to find it in our mirror lists
  61.  *
  62.  **/
  63. pascal ComponentResult _URLHelperNewParseState (Handle storage,
  64.                                         StringPtr            aURL,
  65.                                         URLHParseStatePtr    *pState)
  66. {
  67.     OSErr                theErr;
  68.     URLGlobalsHandle    globals = (URLGlobalsHandle) storage;
  69.     URLHMirrorList        *mList;
  70.  
  71.     /**
  72.      ** Check the params
  73.      **/
  74.  
  75.     theErr = paramErr;
  76.     require (aURL != 0 && pState != 0, BadParams);
  77.  
  78.     /**
  79.      ** Create a new parse state struct.  We will return it if we don't
  80.      ** find a matching url in here...
  81.      **/
  82.     
  83.     *pState = (URLHParseStatePtr) NewPtrClear (sizeof(URLHParseState));
  84.     theErr = MemError ();
  85.     if (theErr == noErr && pState == 0)
  86.         theErr = memFullErr;
  87.     nrequire (theErr, FailedToAllocateParseState);
  88.  
  89.     /**
  90.      ** This is a painful process.  Loop over each mirror list and see if there is match
  91.      ** in that mirror list.  Of course, since I'm lazy, we break this up into small little
  92.      ** bits!
  93.      **/
  94.  
  95.     mList = (*globals)->mirrorList;
  96.     theErr = errURLHNoMirrorMatch;
  97.  
  98.     while (mList) {
  99.     
  100.         theErr = PSFindMirrorInList (mList, aURL, *pState);
  101.         require (theErr == noErr || theErr == errURLHNoMirrorMatch, FailedToLookAtMirrorList);
  102.         if (theErr == noErr)
  103.             break;
  104.  
  105.         mList = mList -> next;
  106.     }
  107.     
  108.     nrequire (theErr, FailedToFindMirror);
  109.  
  110.     (*pState) -> mirrorList = mList;
  111.     BlockMove (aURL, (*pState)->baseURL, aURL[0]+1);
  112.  
  113.     return noErr;
  114.  
  115.     /**
  116.      ** Errors
  117.      **/
  118.  
  119. FailedToFindMirror:                                // Nope -- didn't find it! :(
  120.  
  121. FailedToLookAtMirrorList:                        // Something died while looking at the mirror list
  122.  
  123.     DisposPtr ((Ptr) *pState);
  124.  
  125. FailedToAllocateParseState:                        // Could not allocate the parse state...
  126.  
  127. BadParams:
  128.  
  129.     *pState = 0;
  130.     return theErr;
  131. }
  132.  
  133. /**
  134.  * PSFindMirrorInList
  135.  *
  136.  *  Search through on mirror list looking for a match with a provided URL.   Return the
  137.  *  resulting parse state if we find it.  Again, we hand off the work to another routine
  138.  *  to do the true compare...
  139.  *
  140.  **/
  141. OSErr PSFindMirrorInList (URLHMirrorListPtr mList, StringPtr aURL, 
  142.                             URLHParseStatePtr pState)
  143. {
  144.     OSErr            theErr;
  145.     URLHMirrorPtr    mirror;
  146.  
  147.     mirror = mList -> firstMirror;
  148.     while (mirror) {
  149.     
  150.         theErr = PSMatchMirrorToURL (mList, mirror, aURL, pState);
  151.         require (theErr == noErr || theErr == errURLHNoMirrorMatch, FailedToMatchMirror);
  152.         if (theErr == noErr)
  153.             break;
  154.     
  155.         mirror = mirror -> next;
  156.     
  157.     }
  158.  
  159.     return theErr;
  160.     
  161.     /**
  162.      ** Errors
  163.      **/
  164.  
  165. FailedToFindOneInList:
  166.  
  167. FailedToMatchMirror:
  168.  
  169.     return theErr;
  170. }
  171.  
  172. /**
  173.  * PSMatchMirrorToURL
  174.  *
  175.  *  This is where most of the work gets done.  See if the URL we are dealing with will
  176.  *  match the mirror specified.  Parse out the wild cards as we go along.  Ugh city.
  177.  *
  178.  **/
  179. OSErr PSMatchMirrorToURL (URLHMirrorListPtr mList, URLHMirrorPtr mirror, StringPtr aURL, 
  180.                             URLHParseStatePtr pState)
  181. {
  182.     OSErr                theErr;
  183.     parseReplaceList    mirrorReplace;
  184.     Str255                mirrorName;
  185.  
  186.     /**
  187.      ** First, parse through the mirror list to find the places where we need to do the
  188.      ** replacements...
  189.      **/
  190.  
  191.     theErr = MBGetPString (mList->stringBuffer, mirror->oName, mirrorName);
  192.     nrequire (theErr, FailedToGetMirrorName);
  193.     theErr = PSParseMirrorName (mirrorName, &mirrorReplace);
  194.     nrequire (theErr, FailedToParseMirrorName);
  195.  
  196.     /**
  197.      ** Ok -- next job is to see if the mirror name matches the string...
  198.      **/
  199.     
  200.     theErr = PBParseMatches (mirrorName, &mirrorReplace, aURL, pState);
  201.  
  202.     return theErr;
  203.     
  204.     /**
  205.      ** Errors
  206.      **/
  207.  
  208. FailedToParseMirrorName:            // Ouch -- couldn't parse the mirror name!
  209.  
  210. FailedToGetMirrorName:                // Couldn't extract mirror name from string buffer!
  211.  
  212.     return theErr;
  213. }
  214.  
  215. /**
  216.  * PSParseMirrorName
  217.  *
  218.  *  This routine will find all the "^n"s in the string we have been given and fill in the
  219.  *  replacement item list.  Error if there are more than MAX_PARSE_ITEMS in the list.
  220.  *
  221.  **/
  222. OSErr PSParseMirrorName (StringPtr mirrorName, parseReplaceListPtr list)
  223. {
  224.     OSErr    theErr;
  225.     char    *p, *endString;
  226.     short    itemsUsed;
  227.  
  228.     /**
  229.      ** Loop through the string looking for our special replacement character, "^".
  230.      **/
  231.     
  232.     itemsUsed = 0;
  233.     p = (char *) &(mirrorName[1]);
  234.     endString = (char *) &(mirrorName[mirrorName[0]]);
  235.     while (p <= endString) {
  236.  
  237.         if (*p == SUB_CHAR) {
  238.             p++;
  239.             require (p <= endString, FailedToGetReplIndex);        
  240.             require (itemsUsed < MAX_PARSE_ITEMS, TooManyReplacementItems);
  241.             require ((*p - '0') < MAX_PARSE_ITEMS, BadReplacementChar);
  242.             
  243.             list->itemList[itemsUsed].repID = (*p - '0');
  244.             list->itemList[itemsUsed].offset = p - (char *) &(mirrorName[1]) - 1;
  245.             list->itemList[itemsUsed].length = 2;
  246.             itemsUsed++;
  247.         }
  248.  
  249.         p++;    
  250.     }
  251.     
  252.     /**
  253.      ** Put it all together
  254.      **/
  255.  
  256.     list->validItems = itemsUsed;
  257.  
  258.     return noErr;
  259.     
  260.     /**
  261.      ** Errors
  262.      **/
  263.  
  264. BadReplacementChar:
  265.     theErr = errURLHBadReplChar;
  266.     goto BadReplacement;
  267.  
  268. TooManyReplacementItems:
  269.     theErr = errURLHTooManyReplacements;
  270.     goto BadReplacement;
  271.  
  272. FailedToGetReplIndex:
  273.     theErr = errURLHNameEndsWithReplChar;
  274.  
  275. BadReplacement:
  276.     list -> validItems = 0;
  277.     
  278.     return theErr;
  279. }
  280.  
  281. /**
  282.  * PBParseMatches
  283.  *
  284.  *  This routine will move through the urlString and see if it matches the given mirror
  285.  *  string (with its various parses...  This is a true search, so we have to back up and
  286.  *  back track when we mess up (it is most ugly).
  287.  *
  288.  *  It fills in a parse state with the offsets to the parts of the urlString that
  289.  *  match ^0, ^1, etc. etc.
  290.  *
  291.  **/
  292. OSErr PBParseMatches (StringPtr name, parseReplaceListPtr list, StringPtr urlString,
  293.                         URLHParseStatePtr pState)
  294. {
  295.     OSErr    theErr;
  296.     short    urlOffset[MAX_PARSE_ITEMS+2];
  297.     short    currentItem, index;
  298.     short    currentOffset;
  299.     short    searchStart[MAX_PARSE_ITEMS+2];
  300.     short    searchLength[MAX_PARSE_ITEMS+2];
  301.  
  302.     /**
  303.      ** Figure out what the start and length of each string in the list is.. 
  304.      **/
  305.  
  306.     index = 0;
  307.     if (list->itemList[0].offset != 0) {
  308.         searchStart[index] = 0;
  309.         searchLength[index] = list->itemList[0].offset;
  310.         index++;
  311.     }
  312.  
  313.     for (currentItem = 0; currentItem < list->validItems; currentItem++) {
  314.  
  315.         searchStart[index] = list->itemList[currentItem].offset
  316.                     + list->itemList[currentItem].length;
  317.         if (currentItem+1 < list->validItems) {
  318.             searchLength[index] = list->itemList[currentItem+1].offset
  319.                     - searchStart[index];
  320.         } else {
  321.             searchLength[index] = name[0] - searchStart[index];
  322.         }
  323.         index++;
  324.     }
  325.     index --;
  326.     if (searchLength[index] == 0)            // Take care of end case...
  327.         index--;
  328.  
  329.     /**
  330.      ** Ok.  We now know all the strings we have to search for.  So, look for the first,
  331.      ** if we find it then look for the second, etc. etc.  If we fail to find the second,
  332.      ** look for the first someplace byond where we found it the first time... etc. etc.
  333.      ** This would have been way simpler to do in SNOBAL, durnit!
  334.      **/
  335.  
  336.     currentItem = 0;
  337.     currentOffset = 0;
  338.  
  339.     while (currentItem <= index 
  340.                 && currentItem >= 0) {
  341.     
  342.         /**
  343.          ** Search for the current string starting at the current offset...
  344.          **/
  345.         
  346.         theErr = PSFindString (urlString, currentOffset, &(urlOffset[currentItem]),
  347.                     name, searchStart[currentItem], searchLength[currentItem]);
  348.         if (theErr != noErr) {
  349.             currentItem--;                            // Couldn't find the string.  Back up...
  350.             if (currentItem >= 0)
  351.                 currentOffset = urlOffset[currentItem] + 1;
  352.         } else {
  353.             currentOffset = urlOffset[currentItem]+searchLength[currentItem];
  354.             currentItem++;
  355.         }
  356.     
  357.     }
  358.  
  359.     theErr = noErr;
  360.     if (currentItem < 0)
  361.         theErr = errURLHNoMirrorMatch;
  362.     nrequire (theErr, FailedToDoThisMatch);
  363.  
  364.     /**
  365.      ** Ok -- if we have a good match, then we had better fill in the parse state...
  366.      **/
  367.  
  368.     pState -> validItems = index;
  369.     index = 0;
  370.     if (list->itemList[0].offset == 0) {
  371.         pState->itemList[0].offset = 0;
  372.         pState->itemList[0].length = urlOffset[0] - 1;
  373.         pState->itemList[0].repID = list->itemList[0].repID;
  374.         index++;
  375.     }
  376.  
  377.     for (currentItem = 0; currentItem <= pState->validItems; currentItem++) {
  378.  
  379.         pState->itemList[index].repID = list->itemList[index].repID;
  380.         pState->itemList[index].offset = urlOffset[currentItem] + searchLength[currentItem] - 1;
  381.         if (currentItem+1 <= pState->validItems) {
  382.             pState->itemList[index].length = urlOffset[index+1] - pState->itemList[index].offset;
  383.         } else {
  384.             pState->itemList[index].length = urlString[0] - pState->itemList[index].offset;
  385.         }
  386.         index++;
  387.     }
  388.     pState->validItems = list->validItems;
  389.     return theErr;
  390.     
  391.     /**
  392.      ** Errors
  393.      **/
  394.  
  395. FailedToDoThisMatch:
  396.  
  397.     pState->validItems = 0;
  398.  
  399.     return theErr;
  400.  
  401. }
  402.  
  403. /**
  404.  * PSFindString
  405.  *
  406.  *  This dude will find some part of findString in string.  It will start the search at offset
  407.  *  findOffset.  findOffset==0 means the first character in the string.
  408.  *
  409.  **/
  410. OSErr PSFindString (StringPtr string, short startOffset, short *foundOffset,
  411.                         StringPtr findString, short findOffset, short findLength)
  412. {
  413.     char *p, *endString;
  414.     char *search, *endSearch, *s;
  415.  
  416.     if (findLength == 0)
  417.         return noErr;
  418.  
  419.     p = (char *) string + 1 + startOffset;
  420.     endString = (char *) string + string[0] + 1;
  421.     
  422.     search = (char *) findString + 1 + findOffset;
  423.     endSearch = (char *) findString + 1 + findLength;
  424.     s = search;
  425.  
  426.     while (p < endString) {
  427.     
  428.         if (*s != *p) {
  429.             if (s != search) {            // Backtrack if we need to here...
  430.                 p -= s - search - 1;
  431.                 s = search;
  432.             }
  433.         } else {
  434.             if (s == search)
  435.                 *foundOffset = p - (char *) string;
  436.             s++;
  437.             if (s > endSearch)
  438.                 return noErr;
  439.         }
  440.         p++;
  441.     }
  442.  
  443.     *foundOffset = 0;
  444.     return genericError;
  445. }
  446.  
  447.  
  448. /**
  449.  * _URLHelperDisposeParseState
  450.  *
  451.  *  Kill of the struct we use to do the parseing and prelacing...
  452.  *
  453.  **/
  454. pascal ComponentResult _URLHelperDisposeParseState (Handle storage,
  455.                                         URLHParseStatePtr    pState)
  456. {
  457.     OSErr                theErr;
  458.  
  459.     /**
  460.      ** Make sure the pState isn't zero!
  461.      **/
  462.  
  463.     theErr = paramErr;
  464.     require (pState != 0, BadParams);
  465.  
  466.     DisposePtr ((Ptr) pState);
  467.  
  468.     return noErr;
  469.     
  470.     /**
  471.      ** Errors
  472.      **/
  473.  
  474. BadParams:
  475.  
  476.     return theErr;
  477. }
  478.  
  479. /**
  480.  * _URLHelperGetMirrorRep
  481.  *
  482.  *  Given a valid parse state, find a replacement mirror for ourselves and sub in the
  483.  *  wild-card strings.  Return an error if the caller has indexed past the end of the
  484.  *  mirror list.
  485.  *
  486.  *  index starts at 1!!!!!
  487.  **/
  488. pascal ComponentResult _URLHelperGetMirrorRep (Handle storage,
  489.                                         URLHParseStatePtr    pState,
  490.                                         StringPtr            newURL,
  491.                                         short                index)
  492. {
  493.     OSErr                theErr;
  494.     URLGlobalsHandle    globals = (URLGlobalsHandle) storage;
  495.     URLHMirrorPtr        aMirror;
  496.     Str255                mName;
  497.     parseReplaceList    mReplacements;
  498.     char                *np, *nend;
  499.     char                *mp, *mend;
  500.     char                *rp, *rend;
  501.     short                index;
  502.     short                replacementID;
  503.  
  504.     /**
  505.      ** First job is to make sure that the incomming
  506.      ** parameters are indeed working a-ok.
  507.      **/
  508.  
  509.     theErr = paramErr;
  510.     require (pState != 0 && newURL != 0 && index > 0, BadParams);
  511.  
  512.     /**
  513.      ** First thing to do is loop over the mirrors in the mirror list
  514.      ** and find the one our caller wants to use
  515.      **/
  516.     
  517.     theErr = URLHGetUsableMirror ((*globals)->self, (URLHMirrorListRef) pState->mirrorList, 
  518.                                         (URLHMirrorRef *) &aMirror, index);
  519.     nrequire (theErr, FailedToGetMirror);
  520.  
  521.     /**
  522.      ** Next, parse out the replacement operators...
  523.      **/
  524.     
  525.     theErr = MBGetPString (pState->mirrorList->stringBuffer, aMirror->oName, mName);
  526.     nrequire (theErr, FailedToGetMirrorName);
  527.     theErr = PSParseMirrorName (mName,  &mReplacements);
  528.     nrequire (theErr, FailedToGetReplPos);
  529.  
  530.     /**
  531.      ** Now, copy the mirror name into the new url thing, and replace the "^0" with
  532.      ** the appropriate stuff...!!!  We replace with a "blank" string if we can't find
  533.      ** the replacemnt ID someplace in base URL guy!
  534.      **/
  535.  
  536.     np = (char *) newURL + 1;
  537.     nend = (char *) newURL + sizeof (Str255);
  538.     
  539.     mp = (char *) mName + 1;
  540.     mend = (char *) mName + 1 + mName[0];
  541.  
  542.     while (mp < mend
  543.             && np < nend) {
  544.  
  545.         if (*mp == SUB_CHAR) {                                        // Ok -- we have to sub something in!
  546.             replacementID = *(++mp) - '0';
  547.  
  548.             index = 0;                                                // Find the repl id info
  549.             while (index < pState->validItems) {
  550.                 if (pState->itemList[index].repID == replacementID)
  551.                     break;
  552.                 index++;
  553.             }
  554.             
  555.             if (index < pState->validItems) {                        // Got it, sub it in
  556.                 rp = (char *) pState->baseURL + 1
  557.                                 + pState->itemList[index].offset;
  558.                 rend = rp + pState->itemList[index].length;
  559.                 while (rp < rend && np < nend)
  560.                     *np++ = *rp++;
  561.             }
  562.             
  563.             mp++;                                                    // Get past rep id.
  564.             
  565.         } else {
  566.             *np++ = *mp++;
  567.         }
  568.  
  569.     }
  570.     
  571.     /**
  572.      ** Fix up the length byte and we are done!
  573.      **/
  574.     
  575.     newURL[0] = np - (char *) newURL - 1;
  576.  
  577.     return noErr;
  578.  
  579.     /**
  580.      ** Errors
  581.      **/
  582.  
  583. FailedToGetReplPos:
  584.  
  585. FailedToGetMirrorName:
  586.  
  587. FailedToGetMirror:
  588.  
  589. BadParams:
  590.  
  591.     newURL[0] = 0;
  592.  
  593.     return theErr;
  594. }
  595.